Flutter Display Widgets Conditionally
Our application have a bug, it crashes when all questions are answered. As shown below
Instead of crashing the app, it would be nice if we display some message when the questions list is exhausted. Here, I’ll output a different widget once all questions are answered.
Here we’ll use inline if condition to display either questions and answers or a custom text once questions are exhaused.
Inline if else is a single line statement where we have a condition then after question mark is the true scenario execution and after colon is the false scenario execution.
var num1 = 10;
var num2 = 20;
//normal if else statement
if ( num1 > num2 )
print('Number 1 is greater');
else
print('Number 2 is greater');
//single line if else statement
( num1 > num2 ) ? print('Number 1 is greater') : print('Number 2 is greater');
//both will have the same output, Number 2 is greater.
Here is our main.dart code where we have implemented inline if else statement and show widgets conditionally. We stop showing questions and answers once all the questions have been answered.
//File Name: main.dart
import 'package:flutter/material.dart';
/* ./ means look in the main folder where main.dart resides and then the
* name of the file which you wana import
*/
import './question.dart';
import './answer.dart';
/*
* The arrow function allows us to create a simplified function consisting of a single expression.
* We can omit the curly brackets and the return keyword
*/
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
/*
* createState() is a method that takes no arguments but returns a State object
* which is connected to a StatefulWidget
*/
@override
State<StatefulWidget> createState() {
return _MyAppState();
} //createState()
} //StatefulWidget
/* A leading underscore makes the property private, here leading underscore will turn
* this public class in to private class. Now MyAppState class can only be used in
* main.dart file, in our case in MyApp class.
*/
class _MyAppState extends State<MyApp> {
var _questionIndex = 0;
final questions = const [
{
'questionText': 'What\'s your favorite color?',
'answers': ['Black', 'Red', 'Green', 'White'],
},
{
'questionText': 'What\'s your favorite animal?',
'answers': ['Leopard', 'Markhor', 'Deer', 'Rabbit'],
},
{
'questionText': 'What\'s your favorite lake?',
'answers': ['Saif-ul-Malook', 'Doodi pat sar', 'Katora', 'Saral'],
},
];
void _answerQuestion() {
setState(() {
_questionIndex++;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
/*
* A Scaffold Widget provides a framework which implements the basic material design visual
* layout structure of the flutter app.
*/
home: Scaffold(
appBar: AppBar(
title: Text('First Flutter App'),
),
body: _questionIndex < questions.length
? Column(
children: <Widget>[
Question(
questions[_questionIndex]['questionText'],
),
/*
* for answers, transform list of maps in to list of widgets
* map() method executes a function (anonymous) which we have to pass as an argument.
* it returns a widget
* ... is spread operator, it takes a list and pulls all the values in the list out of it
* and add them to surrounding list as individual value. Means, we don't add a list to a list
* but values of the list to a list.
*/
...(questions[_questionIndex]['answers'] as List<String>)
.map((answer) {
return Answer(_answerQuestion, answer);
}).toList(),
],
)
: Center(child: Text('All caught up')),
),
);
} //build()
} //MyApp()
//FileName: question.dart
import 'package:flutter/material.dart';
class Question extends StatelessWidget {
/*
* final keywords tells dart that this value will never change after its
* initialization here int he constructor
*/
final String questionText;
/* Lets have a constructor to initialize this widget class questionText
* Now the first positional argument passed to this constructor will be stored in
* questionText variable
*/
Question(this.questionText);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity, //double.infinity means occupy the whole screen (width in this case)
margin: EdgeInsets.all(10), //apply margin of 10 pts to all 4 sides
child: Text(
questionText,
style: TextStyle(fontSize: 26),
textAlign: TextAlign.center,
), //Text
); //Container()
} //build
} //Question
//FileName: answer.dart
import 'package:flutter/material.dart';
class Answer extends StatelessWidget {
final Function selectHandler;
final String answerText;
Answer(this.selectHandler, this.answerText);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity, ////double.infinity means occupy the whole screen (width in this case)
margin: EdgeInsets.all(10), //apply margin of 10 pts to all 4 sides
/*
* We have provided 3 named arguments here to ElevatedButton()
* 1. child: which is a Text() widget
* 2. onPressed: which is a function we get via constructor
* 3. style: where we change the color, padding and text style
*/
child: ElevatedButton(
child: Text(answerText),
//onPressed takes a function
onPressed: selectHandler,
style: ElevatedButton.styleFrom(
primary: Colors.blue, //the button's background color
onPrimary: Colors.white, //the button's text color
padding: EdgeInsets.symmetric(horizontal: 50, vertical: 20),
textStyle: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
), //.styleFrom
), //ElevatedButton
); //Container
}
}
Inline if else was a simple addition in our app code where we showed widgets conditionally and made the app more user friendly.
Happy Coding!